导航菜单

GitHub

RichEditor

基于原生EditText+span实现的Android富文本编辑器

组件描述

该组件是基于原生EditText+span的方式实现的,旨在提供一个功能齐全且使用方便的Android富文本编辑器。主要支持了加粗斜体等行内样式、标题引用等段内样式以及插入图片视频甚至自定义View等。

功能演示

功能列表 支持加粗、斜体、删除线、下划线行内样式 支持插入标题、引用段内样式 支持插入段落图片、视频 支持插入段落自定义布局 支持视频、gif和长图标记 支持图片圆角 支持图片视频及自定义View的点击事件 undo redo 支持行内ImageSpan,如类似微博@xxx,#话题名# 支持清除样式 编辑器内部复制粘贴ImageSpan(任意以ImageSpan方式插入的的类型,如图片、视频、自定义view等)如何使用gradle

Step 1. Add the JitPack repository in your root build.gradle at the end of repositories:

allprojects {repositories {...maven { url 'https://jitpack.io' }}}

Step 2. Add the dependency in your app build.gradle:

dependencies {implementation 'com.github.yuruiyin:RichEditor:latest-version'}参数定义 自定义属性名字 参数含义 editor_show_video_mark 是否显示视频标识图标 editor_video_mark_resource_id 视频图标资源id editor_show_gif_mark 是否显示gif标识图标 editor_show_long_image_mark 是否显示长图标识 editor_image_radius 图片和视频圆角大小editor_headline_text_size 标题字体大小 代码演示

说明:各个样式按钮的layout由调用方自行完成

1) 首先在xml中引用RichEditText:2) 针对加粗、斜体、标题等需要修改图标样式的按钮(不包括插入图片按钮),处理如下:// 加粗, 仅包含图标(见demo)val styleBtnVm = StyleBtnVm.Builder().setType(RichTypeEnum.BOLD).setIvIcon(ivBold).setIconNormalResId(R.mipmap.icon_bold_normal).setIconLightResId(R.mipmap.icon_bold_light).setClickedView(ivBold).build()richEditText.initStyleButton(styleBtnVm)// 标题,包含图标和文字(图标高亮的同时文字也要高亮)val styleBtnVm = StyleBtnVm.Builder().setType(RichTypeEnum.BLOCK_HEADLINE) // 指定为段落标题类型.setIvIcon(ivHeadline)// 图标ImageView,用于修改高亮状态.setIconNormalResId(R.mipmap.icon_headline_normal) // 正常图标资源id.setIconLightResId(R.mipmap.icon_headline_light)// 高亮图标资源id.setClickedView(vgHeadline) // 指定被点击的view.setTvTitle(tvHeadline) // 按钮标题文字.setTitleNormalColor(ContextCompat.getColor(this@MainActivity, R.color.headline_normal_text_color)) // 正常标题文字颜色.setTitleLightColor(ContextCompat.getColor(this@MainActivity, R.color.headline_light_text_color))// 高亮标题文字颜色.build()richEditText.initStyleButton(styleBtnVm)3)插入图片或视频/** * 处理插入图片 */private fun handleAddImage() {val intent = Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI)startActivityForResult(intent, GET_PHOTO_REQUEST_CODE)}private fun doAddBlockImageSpan(realImagePath: String, blockImageSpanObtainObject: IBlockImageSpanObtainObject, isFromDraft: Boolean = false) {val blockImageSpanVm = BlockImageSpanVm(blockImageSpanObtainObject) // 不指定宽高,使用图片原始大小(但组件内对最大宽和最大高还是有约束的)//val blockImageSpanVm = BlockImageSpanVm(blockImageSpanObtainObject, imageWidth, imageMaxHeight) // 指定宽高blockImageSpanVm.isFromDraft = isFromDraftrichEditText.insertBlockImage(realImagePath, blockImageSpanVm) { blockImageSpan ->val spanObtainObject = blockImageSpan.blockImageSpanVm.spanObjectwhen (spanObtainObject) {is ImageVm -> {Toast.makeText(this, "短按了图片-当前图片路径:${spanObtainObject.path}", Toast.LENGTH_SHORT).show()}is VideoVm -> {Toast.makeText(this, "短按了视频-当前视频路径:${spanObtainObject.path}", Toast.LENGTH_SHORT).show()}}}}override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)if (requestCode == GET_PHOTO_REQUEST_CODE && resultCode == RESULT_OK && data != null) {// 相册图片返回val selectedImageUri = data.data ?: returnval realImagePath = FileUtil.getFileRealPath(this, selectedImageUri) ?: returnval fileType = FileUtil.getFileType(realImagePath) ?: returnwhen (fileType) {FileTypeEnum.STATIC_IMAGE, FileTypeEnum.GIF -> {val imageVm = ImageVm(realImagePath, "2")doAddBlockImageSpan(realImagePath, imageVm)}FileTypeEnum.VIDEO -> {// 插入视频封面val videoVm = VideoVm(realImagePath, "3")doAddBlockImageSpan(realImagePath, videoVm)}}}}4) 插入自定义布局/** * 插入游戏 */private fun handleAddGame() {val gameVm = GameVm(1, "一起来捉妖")doAddGame(gameVm)}private fun doAddGame(gameVm: GameVm, isFromDraft: Boolean = false) {val gameItemView = layoutInflater.inflate(R.layout.editor_game_item, null)val ivGameIcon = gameItemView.findViewById(R.id.ivGameIcon)val tvGameName = gameItemView.findViewById(R.id.tvGameName)ivGameIcon.setImageResource(R.mipmap.icon_game_zhuoyao)tvGameName.text = gameVm.nameivGameIcon.layoutParams.width = gameIconSizeivGameIcon.layoutParams.height = gameIconSizeval gameItemWidth = getEditTextWidthWithoutPadding()ViewUtil.layoutView(gameItemView, gameItemWidth, gameItemHeight)val blockImageSpanVm = BlockImageSpanVm(gameVm, gameItemWidth, imageMaxHeight)blockImageSpanVm.isFromDraft = isFromDraftrichEditText.insertBlockImage(ViewUtil.getBitmap(gameItemView), blockImageSpanVm) { blockImageSpan ->val retGameVm = blockImageSpan.blockImageSpanVm.spanObject as GameVm// 点击游戏itemToast.makeText(this, "短按了游戏:${retGameVm.name}", Toast.LENGTH_SHORT).show()}}

说明:插入自定义布局最终也是通过bitmap以ImageSpan的形式插入到编辑器中的。

5)获取数据// 返回的编辑器实体是一个list,list中每个元素代表一个段落block,具体block参数可以参考RichEditorBlock, // 但是若需要保存草稿功能,则需要对该list进行转换成自己的实体,否则List序列化后反序列化会丢失数据,可以参考demoval content: List = richEditText.content具体使用请参考demo相关引用设置EditText的光标高度: LineHeightEditText设置图片圆角: RoundedImageViewundo redo: AndroidEdit最后

您的star是我把组件做到完美的动力~

相关推荐: